package com.tfswx.fljdmxyy.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.http.*;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;

import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * http工具
 *
 * @Author 陈文佳
 * @Date 2023/03/24
 */
@Slf4j
public class HttpUtils {

    public static final ThreadLocal<String> rwt = new ThreadLocal<>();
    private static final String SEND_LOG_PATTERN = "==>  {}  {}";

    private static final RestTemplate restTemplate;

    static {
        // 长连接保持30秒
        PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
        // 总连接数
        pollingConnectionManager.setMaxTotal(1000);
        // 同路由的并发数
        pollingConnectionManager.setDefaultMaxPerRoute(1000);

        HttpClientBuilder httpClientBuilder = HttpClients.custom();
        httpClientBuilder.setConnectionManager(pollingConnectionManager);
        // 重试次数，默认是3次，没有开启
        httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(2, true));
        // 保持长连接配置，需要在头添加Keep-Alive
        httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());

        //        RequestConfig.Builder builder = RequestConfig.custom();
        //        builder.setConnectionRequestTimeout(200);
        //        builder.setConnectTimeout(5000);
        //        builder.setSocketTimeout(5000);
        //
        //        RequestConfig requestConfig = builder.build();
        //        httpClientBuilder.setDefaultRequestConfig(requestConfig);

        //        List<Header> headers = new ArrayList<>();
        //        headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36"));
        //        headers.add(new BasicHeader("Accept-Encoding", "gzip,deflate"));
        //        headers.add(new BasicHeader("Accept-Language", "zh-CN"));
        //        headers.add(new BasicHeader("Connection", "Keep-Alive"));
        //
        //        httpClientBuilder.setDefaultHeaders(headers);

        HttpClient httpClient = httpClientBuilder.build();


        // httpClient连接配置，底层是配置RequestConfig
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        // 连接超时
        clientHttpRequestFactory.setConnectTimeout(50000);
        // 数据读取超时时间，即SocketTimeout
        clientHttpRequestFactory.setReadTimeout(Integer.MAX_VALUE);
        // 连接不够用的等待时间，不宜过长，必须设置，比如连接不够用时，时间过长将是灾难性的
        clientHttpRequestFactory.setConnectionRequestTimeout(200);
        // 缓冲请求数据，默认值是true。通过POST或者PUT大量发送数据时，建议将此属性更改为false，以免耗尽内存。
        // clientHttpRequestFactory.setBufferRequestBody(false);

        restTemplate = new RestTemplate();
        restTemplate.setRequestFactory(clientHttpRequestFactory);
        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
        List<MediaType> supportedMediaTypes = new ArrayList<>();
        supportedMediaTypes.add(MediaType.APPLICATION_JSON);
        supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
        supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
        supportedMediaTypes.add(MediaType.APPLICATION_PDF);
        supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_XML);
        supportedMediaTypes.add(MediaType.IMAGE_GIF);
        supportedMediaTypes.add(MediaType.IMAGE_JPEG);
        supportedMediaTypes.add(MediaType.IMAGE_PNG);
        supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM);
        supportedMediaTypes.add(MediaType.TEXT_HTML);
        supportedMediaTypes.add(MediaType.TEXT_MARKDOWN);
        supportedMediaTypes.add(MediaType.TEXT_PLAIN);
        supportedMediaTypes.add(MediaType.TEXT_XML);
        fastJsonHttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes);
        messageConverters.add(fastJsonHttpMessageConverter);
        for (HttpMessageConverter<?> messageConverter : messageConverters) {
            if ("StringHttpMessageConverter".equals(messageConverter.getClass().getSimpleName())) {
                StringHttpMessageConverter messageConverter1 = (StringHttpMessageConverter) messageConverter;
                messageConverter1.setDefaultCharset(StandardCharsets.UTF_8);
            }
        }
        //        restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
    }

    private HttpUtils() {
    }

    public static RestTemplate getRestTemplate() {
        return restTemplate;
    }

    private static <T> T sendRequest(String url, HttpMethod method, Object param, Class<T> responseType, Object... uriVariables) {
        HttpEntity<Object> requsetHttpEntity = null;
        if (param == null) {
        } else if (param instanceof HttpEntity) {
            HttpEntity<Object> objectHttpEntity = (HttpEntity<Object>) param;
            Object body = objectHttpEntity.getBody();
            HttpHeaders headers = objectHttpEntity.getHeaders();
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.addAll(headers);
            // httpHeaders.add("rwt", "980000");
            requsetHttpEntity = new HttpEntity<>(body, httpHeaders);
        } else {
            if (method == HttpMethod.GET) {
                Map<String, Object> map = JSON.parseObject(JSON.toJSONString(param));
                StringBuilder uri = new StringBuilder();
                String s = "?";
                for (Map.Entry<String, Object> entry : map.entrySet()) {
                    uri.append(s).append(entry.getKey()).append("=").append(entry.getValue());
                    s = "&";
                }
                url += uri;
                param = null;
            }
            HttpHeaders httpHeaders = new HttpHeaders();
            // httpHeaders.add("rwt", "980000");
            requsetHttpEntity = new HttpEntity<>(param, httpHeaders);
        }

        long startTime = System.currentTimeMillis();
        try {
            RestTemplate restTemplate = getRestTemplate();
            RequestCallback requestCallback = restTemplate.httpEntityCallback(requsetHttpEntity, responseType);
            ResponseExtractor<ResponseEntity<T>> responseExtractor = restTemplate.responseEntityExtractor(responseType);
            ResponseEntity<T> execute = restTemplate.execute(url, method, requestCallback, responseExtractor, uriVariables);
            if (execute == null) {
                throw new RuntimeException("没有返回数据");
            }
            return execute.getBody();
        } finally {
            log.info(SEND_LOG_PATTERN, url, System.currentTimeMillis() - startTime);
        }
    }

    public static <T> T postForObject(String url, Class<T> responseType, Object... uriVariables) {
        return postForObject(url, null, responseType, uriVariables);
    }

    public static <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) {
        return sendRequest(url, HttpMethod.POST, request, responseType, uriVariables);
    }

    public static <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) {
        return getForObject(url, null, responseType, uriVariables);
    }

    public static <T> T getForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) {
        return sendRequest(url, HttpMethod.GET, request, responseType, uriVariables);
    }

    public static boolean downloadToOutputStream(String url, OutputStream os) {
        return downloadToOutputStream(url, os, null);
    }

    public static Boolean downloadToOutputStream(String url, OutputStream os, BaseProgressCallback callback) {
        return getRestTemplate().execute(url, HttpMethod.GET, null, response -> {
            HttpHeaders headers = response.getHeaders();
            long contentLength = headers.getContentLength();
            try (InputStream inputStream = response.getBody()) {
                byte[] buffer = new byte[1024 * 1024];
                int len;
                long loaded = 0;
                long oldl = 0;
                while ((len = inputStream.read(buffer)) != -1) {
                    os.write(buffer, 0, len);
                    loaded += len;
                    if (callback != null) {
                        long l = loaded * 100 / contentLength;
                        if (l != oldl) {
                            callback.callback(loaded, contentLength);
                            oldl = l;
                        }
                    }
                }
            }
            return true;
        });
    }

    public interface BaseProgressCallback {
        void callback(long loaded, long total);
    }

    @Slf4j
    public static final class Tyyw {
        private Tyyw() {
        }

        // 统一业务统一请求接口
        private static <T> T sendRequest(String url, HttpMethod method, Object param, Class<T> responseType, Object... uriVariables) {
            HttpEntity<Object> requsetHttpEntity = null;
            String rwt = HttpUtils.rwt.get();
            if (rwt == null) {
                rwt = "default";
                //     UserInfo loginUserInfo = TokenUtil.getLoginUserInfo();
                //     if (loginUserInfo != null) {
                //         String tokenRwt = loginUserInfo.getRwt();
                //         if (StringUtils.isNotBlank(tokenRwt)) {
                //             rwt = tokenRwt;
                //         }
                //     }
            }
            if (param != null) {
                if (param instanceof HttpEntity) {
                    HttpEntity<Object> objectHttpEntity = (HttpEntity<Object>) param;
                    Object body = objectHttpEntity.getBody();
                    HttpHeaders headers = objectHttpEntity.getHeaders();
                    HttpHeaders httpHeaders = new HttpHeaders();
                    httpHeaders.addAll(headers);
                    httpHeaders.add("rwt", rwt);
                    requsetHttpEntity = new HttpEntity<>(body, httpHeaders);
                } else {
                    if (method == HttpMethod.GET) {
                        Map<String, Object> map = JSON.parseObject(JSON.toJSONString(param));
                        StringBuilder uri = new StringBuilder();
                        String s = "?";
                        for (Map.Entry<String, Object> entry : map.entrySet()) {
                            uri.append(s).append(entry.getKey()).append("=").append(entry.getValue());
                            s = "&";
                        }
                        url += uri;
                        param = null;
                    }
                    HttpHeaders httpHeaders = new HttpHeaders();
                    httpHeaders.add("rwt", rwt);
                    requsetHttpEntity = new HttpEntity<>(param, httpHeaders);
                }
            } else {
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.add("rwt", rwt);
                requsetHttpEntity = new HttpEntity<>(null, httpHeaders);
            }

            return HttpUtils.sendRequest(url, method, requsetHttpEntity, responseType, uriVariables);
        }

        // 统一业务的Get请求
        private static <T> T get(String url, Object request, Class<T> responseType, Object... uriVariables) {
            return sendRequest(url, HttpMethod.GET, request, responseType, uriVariables);
        }

        // 统一业务的Post请求
        private static <T> T post(String url, Object request, Class<T> responseType, Object... uriVariables) {
            return sendRequest(url, HttpMethod.POST, request, responseType, uriVariables);
        }

        // post请求
        public static <T> T postForObject(String url, Class<T> responseType, Object... uriVariables) {
            return postForObject(url, null, responseType, uriVariables);
        }

        public static <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables) {
            JSONObject response = null;
            try {
                if ("byte[]".equals(responseType.getSimpleName())) {
                    byte[] bytes = post(url, request, byte[].class, uriVariables);
                    if (bytes == null) {
                        return null;
                    }
                    try {
                        JSONObject jsonObject = JSON.parseObject(new String(bytes));
                        TyywResUtil.getObject(jsonObject, null);
                    } catch (JSONException e) {
                        log.debug("不用管的错误", e);
                    }
                    return (T) bytes;
                }
                response = post(url, request, JSONObject.class, uriVariables);
                return TyywResUtil.getObject(response, responseType);
            } catch (Exception e) {
                log.error("请求统一业务失败：\n地址：{}\n参数：{}\n结果：{}\n", url, JSON.toJSONString(request), JSON.toJSONString(response), e);
                throw e;
            }
        }

        public static <T> List<T> postForList(String url, Class<T> responseType, Object... uriVariables) {
            return postForList(url, null, responseType, uriVariables);
        }

        public static <T> List<T> postForList(String url, Object request, Class<T> responseType, Object... uriVariables) {
            JSONObject response = null;
            try {
                response = post(url, request, JSONObject.class, uriVariables);
                return TyywResUtil.getList(response, responseType);
            } catch (Exception e) {
                log.error("请求统一业务失败：\n地址：{}\n参数：{}\n结果：{}\n", url, JSON.toJSONString(request), JSON.toJSONString(response), e);
                throw e;
            }
        }

        // get请求
        public static <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) {
            return getForObject(url, null, responseType, uriVariables);
        }

        public static <T> T getForObject(String url, Object request, Class<T> responseType, Object... uriVariables) {
            JSONObject response = null;
            try {
                if ("byte[]".equals(responseType.getSimpleName())) {
                    byte[] bytes = get(url, request, byte[].class, uriVariables);
                    if (bytes == null) {
                        return null;
                    }
                    try {
                        JSONObject jsonObject = JSON.parseObject(new String(bytes));
                        TyywResUtil.getObject(jsonObject, null);
                    } catch (JSONException e) {
                        log.debug("不用管的错误", e);
                    }
                    return (T) bytes;
                }
                response = get(url, request, JSONObject.class, uriVariables);
                return TyywResUtil.getObject(response, responseType);
            } catch (Exception e) {
                log.error("请求统一业务失败：\n地址：{}\n参数：{}\n结果：{}\n", url, JSON.toJSONString(request), JSON.toJSONString(response), e);
                throw e;
            }
        }

        public static <T> List<T> getForList(String url, Class<T> responseType, Object... uriVariables) {
            return getForList(url, null, responseType, uriVariables);
        }

        public static <T> List<T> getForList(String url, Object request, Class<T> responseType, Object... uriVariables) {
            JSONObject response = null;
            try {
                response = get(url, request, JSONObject.class, uriVariables);
                return TyywResUtil.getList(response, responseType);
            } catch (Exception e) {
                log.error("请求统一业务失败：\n地址：{}\n参数：{}\n结果：{}\n", url, JSON.toJSONString(request), JSON.toJSONString(response), e);
                throw e;
            }
        }
    }

    private static class TyywResUtil {
        private static final String SUCCESS_FIELD = "success";
        private static final String MESSAGE_FIELD = "message";
        private static final String DATA_FIELD = "data";

        private TyywResUtil() {
        }

        public static <T> T getObject(JSONObject response, Class<T> clazz) {
            boolean success = response.getBooleanValue(SUCCESS_FIELD);
            if (!success) {
                String code = response.getString("code");
                if ("1002".equals(code)) {
                    // todo 统一业务登录失败
                    // throw new RuntimeException(ResultCode.YHMHMMBZQ);
                }
                throw new TyywException(response.getString(MESSAGE_FIELD));
            }
            if (clazz == null) {
                return null;
            }
            if ("Boolean".equals(clazz.getSimpleName())) {
                return (T) response.getBoolean(DATA_FIELD);
            }
            if ("String".equals(clazz.getSimpleName())) {
                return (T) response.getString(DATA_FIELD);
            }
            if ("Integer".equals(clazz.getSimpleName())) {
                return (T) response.getInteger(DATA_FIELD);
            }
            if ("Double".equals(clazz.getSimpleName())) {
                return (T) response.getDouble(DATA_FIELD);
            }
            if ("byte[]".equals(clazz.getSimpleName())) {
                return (T) response.getBytes(DATA_FIELD);
            }
            JSONObject jsonObject = response.getJSONObject(DATA_FIELD);
            return jsonObject != null ? jsonObject.toJavaObject(clazz) : null;
        }

        public static <T> List<T> getList(JSONObject response, Class<T> clazz) {
            boolean success = response.getBooleanValue(SUCCESS_FIELD);
            if (!success) {
                throw new TyywException(response.getString(MESSAGE_FIELD));
            }
            JSONArray jsonArray = response.getJSONArray(DATA_FIELD);
            return jsonArray != null ? jsonArray.toJavaList(clazz) : new ArrayList<>();
        }

    }

    public static class TyywException extends RuntimeException {
        public TyywException(String message) {
            super(message);
        }

        public TyywException(String message, Throwable e) {
            super(message, e);
        }
    }
}
